#pragma once 
#include "Log.hpp"
using namespace MyLogModule;
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h> //sockaddr_in
#include <functional>

enum  Exitcode {
    SOCKET_ERROR,
    BIND_ERROR,
    LISTEN_ERROR,
    ACCEPT_ERROR
};
class Socket;
using SockPtr = std::shared_ptr<Socket>;

class Socket
{
public:
    Socket() = default;
    virtual void socketorDie() = 0;
    virtual void bindorDie(uint16_t port) = 0;
    virtual void listenorDie() = 0;
    virtual SockPtr acceptor(struct sockaddr_in*) = 0;
    virtual int Recv(string* out) = 0;
    virtual int Send(const string& message) = 0;
    virtual void SetSockOpt() = 0;
    void BuildTcpSocket(uint16_t port){
        socketorDie();
        SetSockOpt();
        bindorDie(port);
        listenorDie();
    }
};

const int defaultlistenfd = -1;
const int defaultsocketfd = -1;
#define BACKLOG 8
class TcpSocket : public Socket{
public:
    TcpSocket(int socketfd = defaultsocketfd)
        :_socketfd(socketfd),_listensocketfd(defaultlistenfd)
        {}
    virtual void socketorDie(){
        _listensocketfd = ::socket(AF_INET,SOCK_STREAM,0);
        if (_listensocketfd < 0){
            LOG(LogLevel::ERROR) << "socket create error";
            exit(Exitcode::SOCKET_ERROR);
        }
        LOG(LogLevel::INFO) << "socket create success, listensocetfd is: " << _listensocketfd;
    }
    virtual void bindorDie(uint16_t port){  
        sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = ::htons(port);
        server.sin_addr.s_addr = INADDR_ANY;
        socklen_t len = sizeof(server);
        int n  = ::bind(_listensocketfd,(struct sockaddr*)(&server),len);
        if (n < 0){
            LOG(LogLevel::ERROR) << "bind error";
            exit(Exitcode::BIND_ERROR);
        }
        LOG(LogLevel::INFO) << "bind success";
    }
    virtual void SetSockOpt()
    {
        int opt = 1;
        int n = ::setsockopt(_socketfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
        (void)n;
    }    
    virtual void listenorDie(){
        int n = ::listen(_listensocketfd,BACKLOG);
        if (n < 0){
            LOG(LogLevel::ERROR) << "listen error";
            exit(Exitcode::LISTEN_ERROR);
        }
        LOG(LogLevel::INFO) << "listen success";
    }
    virtual SockPtr acceptor(struct sockaddr_in* client){
        socklen_t len = sizeof(*client);
        _socketfd = ::accept(_listensocketfd,(struct sockaddr*)(client),&len);
        if (_socketfd < 0){
            LOG(LogLevel::ERROR) << "accept error";
            exit(Exitcode::ACCEPT_ERROR);
        }
        LOG(LogLevel::INFO) << "accept success,socketfd is: " << _socketfd;
        return make_shared<TcpSocket>(_socketfd);
    }   
    virtual int Recv(string* out){
        char buffer[4096];
        int n = ::recv(_socketfd,buffer,sizeof(buffer),0);
        if (n > 0){
            buffer[n] = 0;
            *out = buffer;
            return n;
        }
        else return n;
    }
    virtual int Send(const string& message){
        int n = ::send(_socketfd,message.c_str(),message.size(),0);
        return n;
    }
    int Fd(){return _socketfd;}
    void CloseListenfd(){
        ::close(_listensocketfd);
        _listensocketfd = defaultlistenfd;
    }
    void Close(){
        ::close(_socketfd);
        _socketfd = defaultsocketfd;
    }
    ~TcpSocket(){
        if (_listensocketfd != defaultlistenfd)
            CloseListenfd();
        if (_socketfd != defaultsocketfd)
            Close();
    }
private:
    int _socketfd;
    int _listensocketfd;
};